package fr.lip6.meta.ple.constraintsextraction;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;


import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.featureIdentification.Product;
import fr.lip6.meta.ple.constraintsextraction.constraint.And;
import fr.lip6.meta.ple.constraintsextraction.constraint.Constraint;
import fr.lip6.meta.ple.constraintsextraction.constraint.Expression;
import fr.lip6.meta.ple.constraintsextraction.constraint.Leaf;
import fr.lip6.meta.ple.constraintsextraction.constraint.Not;
import fr.lip6.meta.ple.constraintsextraction.constraint.Or;

public class ConstraintsRulesExatrction {

	
	/**
	 * Noting fancy happens here to be compatible with older code
	 */
	public ConstraintsRulesExatrction()
	{
		super();
	}
	
	/**
	 * Entry point for the package constraintextration.
	 * This is where all happens
	 * @param allFeatures
	 * @param allProducts
	 * @return ArrayList of Constraints, so two Expression of features, presence of one's implying the
	 * presence of the other one.
	 */
	public ArrayList<Constraint> extractImplicationRules(Collection<Feature> allFeatures,
			Collection<Product> allProducts){

		ArrayList<Constraint> result = new ArrayList<Constraint>();
		ArrayList<Constraint> baseConstraints = new ArrayList<Constraint>();
		//ArrayList<Constraint> baseConstraintsRemaining = new ArrayList<Constraint>();
		//ArrayList<Constraint> fancyConstraints = new ArrayList<Constraint>();
		
		//First, Only simple constraints like F1 ==> F2
		for (Feature f:allFeatures)
			for (Feature f1: allFeatures){
				if(f != f1){
					if(f.getProdIds().containsAll(f1.getProdIds()) 
							&& !(f.getId().equals("F0")) 
							&& !(f1.getId().equals("F0"))
							)	
					{
						baseConstraints.add(new Constraint(f1,f));
					}
				}
			}
		
		
		/*
		 * Extract Mutual Exclusion Constraint
		 */
		
		//First, Only simple constraints like F1 ==> F2
		for (Feature f:allFeatures)
			for (Feature f1: allFeatures){
				if(f != f1){
					if(chechExclusion(f.getProdIds(),f1.getProdIds()) 
							&& !(f.getId().equals("F0")) 
							&& !(f1.getId().equals("F0"))
							)	
					{
						Expression noF = new Not(new Leaf(f));
						Expression noF1 = new Not(new Leaf(f1));
						
						Constraint c1 =new Constraint(f,noF1);
						Constraint c2=new Constraint(f1, noF);
						if (!baseConstraints.contains(c1)) baseConstraints.add(c1);
						if (!baseConstraints.contains(c2)) baseConstraints.add(c2);
					}
				}
			}
		//baseConstraintsRemaining.addAll(baseConstraints);
		
		//Here we try to reach as many constraints as possible
		//TODO A enfermer dans une boucle jusqu'à ce qu'aucune nouvelle modification soit faite ?
		
		//fancyConstraints.addAll(ConstraintsCombineLeft(baseConstraints,baseConstraintsRemaining));
		//fancyConstraints.addAll(ConstraintsCombineRight(baseConstraints,baseConstraintsRemaining));
		//fancyConstraints.addAll(ConstraintsCombineTransitivity(baseConstraints,baseConstraintsRemaining));
		
		result.addAll(baseConstraints);
		
		//result.addAll(fancyConstraints);
		return result;
		
	}
	
	
	

	private boolean chechExclusion(LinkedHashSet<Integer> prodIds,
			LinkedHashSet<Integer> prodIds2) {
		for (Integer t:prodIds){
			if (prodIds2.contains(t)) return false;
		}
		return true;
	}

	/**
	 * |    F1 => F2
	 * |    F2 => F3
	 * \_______>  F1 => F2 AND F3 
	 * @param baseConstraints
	 * @param baseConstraintsRemaining will be modified, as the function will remove from it used constraints
	 * @return
	 */
	@SuppressWarnings("unused")
	private ArrayList<Constraint> ConstraintsCombineTransitivity(
			ArrayList<Constraint> baseConstraints,
			ArrayList<Constraint> baseConstraintsRemaining) {
		// TODO Auto-generated method stub
		//The major problem with it is handling duplicate expressions on the right side of
		// the constraint
		ArrayList<Constraint> result = new ArrayList();
		//Detecter Expressions similaires
		//factoriser
		return result;
	}

	/**
	 * |    F4 => F1
	 * |    F4 => F2
	 * \_______>  F4 => F1 AND F2 
	 * @param baseConstraints
	 * @param baseConstraintsRemaining will be modified, as the function will remove from it used constraints
	 * @return ArrayList with new constrains equivalent to the base constraint removed
	 */
	@SuppressWarnings("unused")
	private ArrayList<Constraint> ConstraintsCombineRight(
			ArrayList<Constraint> baseConstraints,
			ArrayList<Constraint> baseConstraintsRemaining) {
		
		ArrayList<Constraint> result = new ArrayList();
		
		//Detect similar expression
		//keys are features left on the constraints
		//Value is an array list containing all the feature present if key is present
		HashMap<Feature,ArrayList<Expression>> hm= new HashMap<Feature,ArrayList<Expression>>();
		ArrayList<Expression> current;
		for(Constraint c: baseConstraints)
		{
			if (c.getLeft() instanceof Leaf)
			{
				Leaf currentLeaf = (Leaf) c.getLeft();
				current = hm.get(currentLeaf.getFeature());
				if(current == null)
					current = new ArrayList<Expression>();
				current.add(c.getRight());
				hm.put(currentLeaf.getFeature(),current);
				
				//we remove the redundant constraint if it well be used later. 
				if (current.size()>1)
					baseConstraintsRemaining.remove(c);
			}
		}

		//factorize
		Set<Feature> keys = hm.keySet();
		Expression right;
		for(Feature featureOnLeft: keys)
		{
			//if the constraint is simple we don't use it
			if (hm.get(featureOnLeft).size()<2)
			{
				continue;
			}
			
			//Building right part of the constraint
			right = new And(hm.get(featureOnLeft).remove(0),hm.get(featureOnLeft).remove(0));
			for(Expression r: hm.get(featureOnLeft))
			{
				right = new And(r,right);
			}
			//Finally we add a new constraint to the result
			result.add(new Constraint(featureOnLeft,right));
			
			

		}
		
		return result;
	}

	/**
	 * Not doing anything yet
	 * |    F1 => F4
	 * |    F2 => F4
	 * \_______>  F1 OR F2 => F4 
	 * @param baseConstraints
	 * @param baseConstraintsRemaining will be modified, as the function will remove from it used constraints
	 * @return
	 */
	@SuppressWarnings("unused")
	private ArrayList<Constraint> ConstraintsCombineLeft(
			ArrayList<Constraint> baseConstraints,
			ArrayList<Constraint> baseConstraintsRemaining) {
		// TODO Auto-generated method stub
		ArrayList<Constraint> result = new ArrayList();
		//Detecter Expressions similaires
		//factoriser
		return result;
	}
	
	/**
	 * For debug Purposes, it displays the features list and associated products
	 * @param allFeatures
	 */
	public void displayContext(Collection<Feature> allFeatures)
	{
		for (Feature f:allFeatures){
			System.out.println(f.getId()+" -> " + f.getProdIds());
		}
		System.out.println("");
	}
	public static ArrayList<Constraint> normaLizertheConrtaints(ArrayList<Constraint> rules)
	{
		ArrayList<Constraint> cons=new ArrayList<Constraint>();
		ArrayList<Constraint> processcons=new ArrayList<Constraint>();
		for (Iterator<Constraint> iter = rules.iterator(); iter.hasNext();)
		   { 	
			Constraint c=iter.next();
			if(!processcons.contains(c))
			{
			 String res=c.getLeft().toString();
			 int i=0;
			 Or or=new Or(null, null);
			 or.setLeft(c.getLeft());
			// or.setRight(c.getRight());
			 
			for (Iterator<Constraint> iter1 = rules.iterator(); iter1.hasNext();)
			   {
			   Constraint c1=iter1.next();
			  
			   if(c1.getRight().toString().equals(c.getRight().toString()))
			   {
				   if(!c.getLeft().toString().equals(c1.getLeft().toString()))
				   {  
					   or.setLeft(new Or(or.getLeft(), or.getRight()));
					   or.setRight(c1.getLeft());
					   if(!processcons.contains(c1))
					   {
						   processcons.add(c1);
					   }
					 
				       res=res+ " or "+c1.getLeft().toString() +"=>" +c.getRight().toString() ;
				  //    iter1.remove();
				      i=1;
				   }
			   }
			   
			   }
			
			if(i==1)
			{
			Constraint cc=new Constraint(or, c.getRight());
			cons.add(cc);
			}
			
			if(i==0)
			{
				cons.add(c);
			}
			}
			
			//System.err.println(res);
			
		    }
		for (Iterator<Constraint> it = cons.iterator(); it.hasNext();)
		   { Constraint c2=it.next();
		   if(c2.getLeft() instanceof Or)
			System.err.println("start"+c2.getLeft().toXML()+"end");
		   else
			   System.err.println("start"+c2.getLeft().toXML()+"end");
		   }
		
		return cons;
	}
}


